/*  
 * Video Game Network Library
 * http://gamenetlib.codeplex.com/
 * 
 * Copyright (c) 2011 Joe Hegarty
 *  
 * This source code is subject to the terms of the Microsoft Public License (Ms-PL). 
 *  
 * Redistribution and use in source and binary forms, with or without modification, 
 * is permitted provided that redistributions of the source code retain the above 
 * copyright notices and this file header. 
 * 
 * For details, see <http://www.opensource.org/licenses/ms-pl.html>. 
 *  
 * All other rights reserved. 
 *
 */  

#include "StandardIncludes.hpp"
#include "NetSocketWrapper.hpp"
#include "NetSocketAddress.hpp"


NNetLib::CSocketWrapper::CSocketWrapper() :
	m_socketHandle(INVALID_SOCKET),
	m_blocking(true)
{
}


NNetLib::CSocketWrapper::~CSocketWrapper()
{
	Close();
}

bool NNetLib::CSocketWrapper::Create(u16 portNumber)
{
	m_socketHandle = ::socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);

	if(m_socketHandle != INVALID_SOCKET)
	{
		::sockaddr_in inAddress = { 0 };
		inAddress.sin_family = AF_INET;
		inAddress.sin_addr.s_addr = INADDR_ANY;
		inAddress.sin_port = htons(portNumber);

		int bindResult = ::bind(m_socketHandle, (SOCKADDR*) &inAddress, sizeof(inAddress));
		if(bindResult != SOCKET_ERROR)
		{
			return true;
		}
		else
		{
			Close();
		}
	}

	return false;
}

void NNetLib::CSocketWrapper::Close()
{
	if(m_socketHandle != INVALID_SOCKET)
	{
		::closesocket(m_socketHandle);
		m_socketHandle = INVALID_SOCKET;
		m_blocking = true;
	}
}

bool NNetLib::CSocketWrapper::SetBlocking(bool blocking)
{
	if(m_socketHandle != INVALID_SOCKET)
	{
		if(blocking != m_blocking)
		{
			DWORD nonBlocking = blocking ? 0 : 1;
			int setBlockingResult = ::ioctlsocket(m_socketHandle, FIONBIO, &nonBlocking);

			if(setBlockingResult == ERROR_SUCCESS)
			{
				return true;
			}
		}
	}

	return false;
}

bool NNetLib::CSocketWrapper::Send(const CSocketAddress& destinationAddress, const char* p_data, u32 dataSize)
{
	if(m_socketHandle != INVALID_SOCKET)
	{
		::sockaddr_in destination = { 0 };
		destination.sin_family = AF_INET;
		destination.sin_port = htons(destinationAddress.GetPort());
		destination.sin_addr.s_addr = htonl(destinationAddress.GetAddress());

		int sentLength = ::sendto(m_socketHandle, p_data, dataSize, 0, (sockaddr*)&destination, sizeof(destination));

		return sentLength != SOCKET_ERROR;
	}

	return false;
}

bool NNetLib::CSocketWrapper::Receive(CSocketAddress& outAddress, char* p_outData, u32& outSize, u32 dataBufferSize)
{
	if(m_socketHandle != INVALID_SOCKET)
	{
		::sockaddr_in fromAddress = { 0 };
		int fromAddressLength = sizeof(fromAddress);

		int receivedLength = ::recvfrom(m_socketHandle, p_outData, dataBufferSize, 0, (sockaddr*) &fromAddress, &fromAddressLength);

		if(receivedLength != SOCKET_ERROR && receivedLength > 0)
		{
			outSize = receivedLength;
			outAddress.SetAddress(ntohl(fromAddress.sin_addr.s_addr));
			outAddress.SetPort(ntohs(fromAddress.sin_port));
			return true;
		}
	}

	return false;
}